/**
 * Description: Queue is an ArrayList which holds values of type
 *              that defined by the user.
 */

//import java utilities
  import java.util.*;

public class Queue {
    //instance variables
      private ArrayList<Process> myQueue;
      public int allocationTable[][] = new int [7][2];
      private boolean sorted = false;
      
      private int Available[] = {1, 1, 1, 1, 1, 1, 1};
      //There can be a maximum of 1000 processes in the waiting queue at once
      private int Max[][] = new int [1000][7];
      private int Allocation[][] = new int [1000][7];
      private int Need[][] = new int [1000][7];
      private int Request[][] = new int [1000][7];
    
    //constructor: creates an ArrayList of type Process
      public Queue() {
        //creat a new ArrayList
          myQueue = new ArrayList<Process>();
      }
    
    //isEmpty: returns true if the queue is empty, false otherwise
      public boolean isEmpty() {
        //checks if the ArrayList is empty
          return myQueue.isEmpty();
      }
      
    //clear: clears the queue of all its elements
      public void clear() {
          //clears using the method clear() from ArrayList
            myQueue.clear();
      }
      
    //whoIsNext: returns the element at the front of the list
      public Process whoIsNext() {
          //returns the head of the queue using get() defined in ArrayList
            return myQueue.get(0);
      }
      
    //addToBack: add a new element of the base class to the end of the queue
      public void addToBack(Process object) {
        //uses the definition of add from ArrayList to add elements
          myQueue.add(object);
	  sorted = false;
       }
      
    //removeFromFront: returns the element at the front of the list and removes
    //                 it. Else, return null
      public Process removeFromFront() {
          //check if the ArrayList is empty
            if(!isEmpty())
            {//remove the element at the head                
                return myQueue.remove(0);
            }
            else
                return null;
      }
      
      public Process remove(int i)
      {
                //check if the ArrayList is empty
            if(!isEmpty())
            {//remove the element at the head                
                return myQueue.remove(i);
            }
            else
                return null;
      }
      
      public Process nextElement(int i)
      {
      //returns the element at the specified position in the array
      return myQueue.get(i);
      }
      
      public int size()
      {
      return myQueue.size();
      }

      public void swap(int indexa, int indexb)
      {
      Process temp;
      temp=myQueue.get(indexa);
      myQueue.set(indexa, myQueue.get(indexb));
      myQueue.set(indexb, temp);
      }
      
      public int schedule()
      {
      int j;
      Process value;
     /* System.out.println("UNSORTED");
      
      for(int x=0;x<myQueue.size();x++)
      {
      System.out.println(myQueue.get(x).pcb.p_number);
      }*/
      
      	for(int i=0;i<myQueue.size();i++)
      	{
		//set value to the current element 
		value=myQueue.get(i);
		//check all values previous to position i
		j=i-1;
		while((j>=0)&&((myQueue.get(j)).pcb.burstLengthRemaining > value.pcb.burstLengthRemaining))
		{
		swap(j, j+1);
		--j;
		}
      	}
	/*System.out.println("SORTED NEW QUEUE:");
	      for(int x=0;x<myQueue.size();x++)
      {
      System.out.println(myQueue.get(x).pcb.p_number);
      }*/
      	sorted = true;
	return 0;
      }
      
      public boolean isSorted(){
      	return sorted;
      }
      
      public boolean bankers(){
    	  boolean resourcesAvailable = true;
    	  //boolean done = true;
    	  int Work[] = Available;
    	  boolean Finish[] = new boolean[1000];
    	  
    	  System.out.println("here");
    	  //Step 1
    	  for(int n = 0; n < myQueue.size(); n++){
    		  for(int m = 0; m < 7; m++){
    			  if(Allocation[n][m] != 0){
    				  Finish[n] = false;
    				  System.out.println(n+" is FALSE");
    			  }
    			  else{
    				  Finish[n] = true;
    				  System.out.println(n+" is TRUE");
    			  }
    		  }
    	  }
    	  
    	  for(int k = 0; k < myQueue.size(); k++){
    		  //Step 2
    		  for(int j = 0; j < Work.length && resourcesAvailable; j++){
    			  if(Need[k][j] > Work[j]){
    				  resourcesAvailable = false;
    				  System.out.println(k+" can not allocate resources");
    			  }
    		  }
    		  
    		  //Step 3
    		  if(!Finish[k] && resourcesAvailable){
    			  for(int j = 0; j < Work.length; j++){
	    			  Work[j] = Work[j]+Allocation[k][j];
	    		  }
    			  System.out.println(k+" could allocate and is giving back");
    			  Finish[k] = true;
    		  }
    	  }
    	  
    	  //Step 4
    	  for(int i = 0; i < myQueue.size(); i++){
    		  if(!Finish[i]){
    			  //Deadlocked State
    			  System.out.println(i+" is TOTALLY FALSE");
    			  return false;
    		  }
    	  }
    	  System.out.println("EVERYTHING is TOTALLY TRUE");
    	  //Safe State
    	  return true;
      }
      
      public void bankers1(){
		int tempAllocationTable[][] = new int[myQueue.size()][7];
		boolean ableToAllocate;
		int devicesNeeded=0;
		//for each process i in the waiting queue
		for(int i = 0; i < myQueue.size(); i++){
			ableToAllocate=true;
			
			//for each device j check if we are able to allocate it
			for(int j = 0; j < 7 && ableToAllocate; j++){

				try{
					devicesNeeded = myQueue.get(i).pcb.devicesNeeded[j]-allocationTable[i][j];
				}catch(ArrayIndexOutOfBoundsException e){
					devicesNeeded = myQueue.get(i).pcb.devicesNeeded[j];
				}
				if(devicesNeeded <1){
					ableToAllocate=false;
				
					//Kernel.deviceTable[j]-= devicesNeeded;	
				}
			}
			
			  if(ableToAllocate)
			  {
			    for(int j = 0; j < myQueue.get(i).pcb.devicesNeeded.length; j++){
			    Kernel.writeToLog("RESOURCE NUMBER ="+j+"was allocated to process number="+myQueue.get(i).pcb.p_number);
			    myQueue.get(i).pcb.devicesInUse[j][0] = 1;
			    myQueue.get(i).pcb.devicesInUse[j][1] = Kernel.time;
			    tempAllocationTable[i][j] += devicesNeeded;
			    allocationTable = tempAllocationTable;
			    }
			  }
		}
		
      }
      
      public void addToWaiting(Process p){
    	  for(int i = 0; i < 7; i++){
    		  Max[myQueue.size()][i] = p.pcb.devicesNeeded[i];
    		  Allocation[myQueue.size()][i] = 0;
    		  Need[myQueue.size()][i] = p.pcb.devicesNeeded[i];
    		  
    		  Kernel.writeToLog("RESOURCE NUMBER ="+i+"was allocated to process number="+p.pcb.p_number);
    		  p.pcb.devicesInUse[i][0] = 1;
    		  p.pcb.devicesInUse[i][1] = Kernel.time;
    	  }
    	  
    	  //myQueue.add(p);
      }
      
      public Process removeFromWaiting(){
    	  for(int i = 0; i < 7; i++){
    		  myQueue.get(0).pcb.devicesInUse[i][0]=0;
    		  Available[i] += 1;
			  Kernel.writeToLog("RESOURCE NUMBER ="+i+"was DEallocated from process ="+myQueue.get(0).pcb.p_number);
    	  }
    	  
    	  for(int i = 0; i < myQueue.size(); i++){
    		  Max[i] = Max[i+1];
    		  Allocation[i] = Allocation[i+1];
    		  Need[i] = Need[i+1];
    	  }
    	  
    	  return myQueue.remove(0);
      }
      
    //toString: displays the contents of the queue
      public String toString() {
          //local variable
            String output = "";
            
          //add each element in myQueue to the output string
            for(Process object : myQueue)
                output+=object + " ";
            
            return output;      //return output string
      }
}

